#include "BarSectionParser.h"

#include <assert.h>

#include <algorithm>
#include <iostream>

#include "DBManager/IService/Input/Material/IIsotropicMaterialService.h"
#include "DBManager/IService/Input/Material/IMaterialSummaryService.h"
#include "DBManager/IService/Input/Property/IPBARLService.h"
#include "DBManager/IService/Input/Property/IPBARService.h"
#include "DBManager/IService/Input/Property/IPBRSECTService.h"
#include "DBManager/IService/Input/Property/IPropertySummaryService.h"

#include "SGFem/BeamSecPropCalculator/Common/BeamSecPropCalculatorInfo.h"
#include "SGFem/BeamSecPropCalculator/FormulaMethod/Interface/BarSectionFormulaCalculatorFactory.h"
#include "SGFem/BeamSecPropCalculator/NumericalMethod/SectionCalculator/ArbitrarySectionTool.h"
#include "SGFem/BeamSecPropCalculator/NumericalMethod/SectionCalculator/SectionCalculator.h"

using SG::Algebra::Matrixd;
using SG::Algebra::Point;
using SG::DataStructure::Common::Id;
using SG::DataStructure::Common::Real;
using SG::DataStructure::Common::Status;
using SG::DataStructure::FEM::BeamMeshedSectionProperty;
using SG::DataStructure::FEM::BeamSectionStandardTypes;
using SG::DataStructure::FEM::IsotropicMaterial;
using SG::DataStructure::FEM::PBARData;
using SG::DataStructure::FEM::PBARLData;
using SG::DataStructure::FEM::PropertyTypes;
using SG::FEMSolver::Element::ArbitrarySectionTool;
using SG::FEMSolver::Element::BarSectionParser;
using SG::FEMSolver::Element::SectionCalculator;
using namespace SG::DBManager;

/** @brief 通过 id 获得梁属性
 *
 * 当首次遇到某种类型的截面，会计算梁的属性
 */
void BarSectionParser::GetSectionProperty (const DBServiceFactorySP dbServiceFactory, Id pId, _OUT PBARData& property)
{
    // 属性号为 `pId` 的截面并非第一次处理，直接从缓存中提取属性
    auto it = m_property.find (pId);
    if (it != m_property.end ())
    {
        property = it->second;
        return;
    }

    // 属性号为 `pId` 的截面首次处理
    // 按照类型(直接指定值，标准形状，任意截面)分别调用相应的计算功能
    PropertyTypes type = dbServiceFactory->get<IPropertySummaryService> ()->GetPropertyType (pId);

    switch (type)
    {
    case PropertyTypes::PROTYPE_PBAR: {
        // 使用梁属性数值定义的截面，直接获取截面属性
        dbServiceFactory->get<IPBARService> ()->FindById (pId, property);
        break;
    }
    case PropertyTypes::PROTYPE_PBARL: {
        // 标准定义形状截面，使用对应计算工具定义
        PBARLData pBarL;
        dbServiceFactory->get<IPBARLService> ()->FindById (pId, pBarL);
        BeamSectionStandardTypes type = pBarL.GetBeamSectionStandardTypes ();

        auto  calculator = SG::FEMSolver::Element::BarSectionFormulaCalculatorFactory::Create (type);
        Point centroid;
        calculator->Compute (pBarL, _OUT property, _OUT centroid);
        break;
    }
    case PropertyTypes::PROTYPE_PBRSECT: {
        // 自定义形状截面
        // 获取数据
        SG::DataStructure::FEM::PBRSECTData pbrsectData;
        dbServiceFactory->get<IPBRSECTService> ()->FindById (pId, pbrsectData);
        SG::DataStructure::FEM::BeamSectionArbitrary boundaryData;
        dbServiceFactory->get<IPBRSECTService> ()->ConvertToBeamSectionArbitrary (pId, boundaryData);

        // 划分网格
        Meshes meshes;
        ArbitrarySectionTool::MeshT6 (boundaryData, _OUT meshes);

        // 计算属性
        BeamMeshedSectionProperty sectionProperty{};
        SectionCalculator::Calculate (meshes, _OUT sectionProperty);
        Matrixd K = sectionProperty.ComputeK ();

        // 写入到PBARData
        property.m_mId = pbrsectData.m_MID;
        property.m_A   = sectionProperty.m_area;
        property.m_I1  = sectionProperty.m_Iy;
        property.m_I2  = sectionProperty.m_Iz;
        property.m_I12 = sectionProperty.m_Iyz;
        property.m_J   = sectionProperty.m_J;
        property.m_K1  = K (1, 1);
        property.m_K2  = K (0, 0);

        break;
    }

    default:
        // 未找到属性定义，处理不了
        std::cout << NOT_FIND_BAR_SECTION_DEFINITION;
        assert (0);
        break;
    }

    // 缓存数据，避免再次查询数据库系统
    m_property[pId] = property;

    // 查询材料数据，并缓存到
    auto materialType = dbServiceFactory->get<IMaterialSummaryService> ()->GetMaterialType (property.m_mId);
    // 仅仅支持线弹性材料
    assert (std::find (materialType.begin (), materialType.end (), SG::DataStructure::FEM::MaterialTypes::MATTYPE_Isotropic)
            != materialType.end ());

    // if (std::find(materialType.begin(), materialType.end(),
    //               static_cast<SG::DataStructure::FEM::MaterialTypes>(SG::DataStructure::FEM::MaterialTypes::MATTYPE_Isotropic))
    //     == materialType.end()) {
    //     throw std::runtime_error("Unsupported material type for property ID: " + std::to_string(pId));
    // }

    IsotropicMaterial material;
    if (Status::STATUS_Failure == dbServiceFactory->get<IIsotropicMaterialService> ()->FindById (property.m_mId, material))
    {
        // 未在数据库中找到材料定义
        assert (0);
    }
    // 不同`属性号pId`的截面具有相同`材料mId`时，后一次的材料属性会对前一次材料属性覆盖。此时不影响计算
    m_mat[property.m_mId] = material;
}

void BarSectionParser::GetMatrial (Id mId, IsotropicMaterial& material) const
{
    auto it = m_mat.find (mId);
    if (it == m_mat.end ())
    {
        // 发生异常的可能情况为：
        // 1. 材料属性号数据出错；
        // 2. 调用本函数前，没有正确调用 GetSectionProperty ()
        assert (0);
        return;
    }

    material = it->second;
}